#ifndef CPU_INSTR_H
#define CPU_INSTR_H

#include "types.h"

static inline void cli(void)
{
    __asm__ __volatile__("cli");
}

static inline void sti(void)
{
    __asm__ __volatile__("sti");
}

static inline uint8_t inb(uint16_t port)
{
    uint8_t rv;

    // inb dx, al
    __asm__ __volatile__(
        "inb %[p], %[v]":[v]"=a"(rv) : [p]"d"(port)
    );
}

static inline uint16_t inw(uint16_t port)
{
    uint16_t rv;

    __asm__ __volatile__(
        "in %[p], %[v]":[v]"=a"(rv) : [p]"d"(port)
    );
}

static inline void outb(uint16_t port, uint8_t data)
{
    // outb al, dx
    __asm__ __volatile__(
        "outb %[v], %[p]"::[p]"d"(port), [v]"a"(data)
    );
}

static inline void lgdt(uint32_t start, uint32_t size)
{
    struct {
        uint16_t limit;     // GDT界限
        uint32_t start;     // GDT起始物理地址
    }__attribute__((packed)) gdt;

    gdt.start = start;
    gdt.limit = size - 1;

    __asm__ __volatile__(
        "lgdt %[g]"::[g]"m"(gdt)
    );
}

static inline void lidt(uint32_t start, uint32_t size)
{
    struct {
        uint16_t limit;     // IDT界限
        uint32_t start;     // IDT起始物理地址
    }__attribute__((packed)) idt;

    idt.start = start;
    idt.limit = size - 1;

    __asm__ __volatile__(
        "lidt %[g]"::[g]"m"(idt)
    );
}

static inline uint32_t read_cr0(void)
{
    uint32_t cr0;
    __asm__ __volatile__(
        "mov %%cr0, %[v]":[v]"=r"(cr0)
    );
    return cr0;
}

static inline void write_cr0(uint32_t v)
{
    __asm__ __volatile__(
        "mov %[v], %%cr0"::[v]"r"(v)
    );
}

static inline uint32_t read_cr2(void)
{
    uint32_t cr2;
    __asm__ __volatile__(
        "mov %%cr2, %[v]":[v]"=r"(cr2)
    );
    return cr2;
}

static inline void write_cr2(uint32_t v)
{
    __asm__ __volatile__(
        "mov %[v], %%cr2"::[v]"r"(v)
    );
}

static inline uint32_t read_cr3(void)
{
    uint32_t cr3;
    __asm__ __volatile__(
        "mov %%cr3, %[v]":[v]"=r"(cr3)
    );
    return cr3;
}

static inline void write_cr3(uint32_t v)
{
    __asm__ __volatile__(
        "mov %[v], %%cr3"::[v]"r"(v)
    );
}

static inline uint32_t read_cr4(void)
{
    uint32_t cr4;
    __asm__ __volatile__(
        "mov %%cr4, %[v]":[v]"=r"(cr4)
    );
    return cr4;
}

static inline void write_cr4(uint32_t v)
{
    __asm__ __volatile__(
        "mov %[v], %%cr4"::[v]"r"(v)
    );
}

static inline void far_jmp(uint32_t selector, uint32_t offset)
{
    uint32_t addr[] = {offset, selector};

    __asm__ __volatile__(
        "ljmpl *(%[a])"::[a]"r"(addr)
    );
}

static inline void hlt(void)
{
    __asm__ __volatile__("hlt");
}

static inline void ltr(uint16_t tss_sel)
{
    __asm__ __volatile__("ltr %%ax"::"a"(tss_sel));
}

static inline uint32_t read_eflags()
{
    uint32_t eflags = 0;
    __asm__ __volatile__(
        "pushf\n\t"
        "pop %%eax":"=a"(eflags)
    );
    return eflags;
}

static inline uint32_t write_eflags(uint32_t eflags)
{
    __asm__ __volatile__(
        "push %%eax\n\t"
        "popf"::"a"(eflags)
    );
}

#endif  // CPU_INSTR_H